Lecture 4
https://github.com/[[Your-username]]/UWBiost561/issues
(putting your username in place of [[Your-username]]).echo=FALSE in the code chunk. I
want to see the code in the HTML document.fig.width
and/or out.width to make your knitted plot to be bigger.
summarize()/summarise() creates a new
tibble that computes one number from a vector (“column”) of
variablesmutate() creates a new variable (“column”, one value
per row) based on the other existing variablesFrom https://www.rstudio.com/wp-content/uploads/2015/02/data-wrangling-cheatsheet.pdf
To see an example, recall our COVID dataset.
## # A tibble: 15,524 × 17
## subject_id fake_first_name fake_last_name gender pan_day test_id clinic_name
## <dbl> <chr> <chr> <chr> <dbl> <chr> <chr>
## 1 1412 jhezane westerling female 4 covid inpatient w…
## 2 533 penny targaryen female 7 covid clinical lab
## 3 9134 grunt rivers male 7 covid clinical lab
## 4 8518 melisandre swyft female 8 covid clinical lab
## 5 8967 rolley karstark male 8 covid emergency d…
## 6 11048 megga karstark female 8 covid oncology da…
## 7 663 ithoke targaryen male 9 covid clinical lab
## 8 2158 ravella frey female 9 covid emergency d…
## 9 3794 styr tyrell male 9 covid clinical lab
## 10 4706 wynafryd seaworth male 9 covid clinical lab
## # ℹ 15,514 more rows
## # ℹ 10 more variables: result <chr>, demo_group <chr>, age <dbl>,
## # drive_thru_ind <dbl>, ct_result <dbl>, orderset <dbl>, payor_group <chr>,
## # patient_class <chr>, col_rec_tat <dbl>, rec_ver_tat <dbl>
covid_testing %>%
mutate(full_name = paste(fake_first_name, fake_last_name)) %>%
select(fake_first_name, fake_last_name, full_name)
pivot_wider() and pivot_longer() functions in
the tidyr package.DESCRIPTION fileThe DESCRIPTION file serves three puposes:
MASS and
mvtnorm package, under Imports:testthat, knitr, and
markdown packages are “suggested” under
Suggests: in the sense that these are useful when making
the vignettes or running the tests, but are not strictly required for
using the package itself.Package: UW561S2025Example
Type: Package
Title: Example package of UW BIOST 561 S2025
Version: 0.1.0
Author: Kevin Lin
Maintainer: Kevin Lin <kzlin@uw.edu>
Description: Describing the EM algorithm for spherical Gaussians.
License: MIT +file LICENSE
Encoding: UTF-8
LazyData: true
RoxygenNote: 7.3.1
URL: https://linnykos.github.io/561_s2025_example/
VignetteBuilder: knitr
Imports:
MASS,
mvtnorm
Suggests:
testthat, knitr, markdown
If we take a look at the glmnet package (to fit a LASSO
regression, see https://cran.r-project.org/web/packages/glmnet/index.html):
This file is the literally in https://github.com/cran/glmnet/blob/master/DESCRIPTION.
R folderrmult.R:ROxygenrmult.R, you see a lot of other
lines before the literal function definition in line 12. What is all
this stuff?This is the documentation for the glmnet::rmult
function.
If you look at what the lines of code starting with the
#' characters in https://github.com/cran/glmnet/blob/master/R/rmult.R
(i.e., lines 1 through 11), you’ll notice it looks strikingly similar to
the documentation.
#' Generate multinomial samples from a probability matrix
#'
#' Generate multinomial samples
#'
#' Simple function that calls the \code{rmultinom} function. It generates a class label
#' for each row of its input matrix of class probabilities.
#'
#' @param p matrix of probabilities, with number of columns the number of classes
#' @return a vector of class memberships
#' @author Trevor Hastie \cr Maintainer: Trevor Hastie <hastie@@stanford.edu>
#' @export rmult
This is because R somehow knew to convert these lines of code into the documentation! This is what ROxygen does – You write the documentation “in-line” with the code, and then R Studio does something magical (which we’ll discuss in a bit) that “converts” this text into a documentation.
@export mean?cleanup_na_matrix(), I
do expect people who use my package might want to use
this function..cleanup_na_vector(), which I do not
expect people will use, so I do not “export” it.#' Clean-up all NAs in a matrix
#'
#' Replace all the NAs in each column with the median value of non-NAs
#' (or all 0's if all values are NAs).
#'
#' @param mat a numeric matrix
#'
#' @return a numeric matrix without NAs
#' @export
cleanup_na_matrix <- function(mat){
stopifnot(is.matrix(mat), all(is.numeric(mat)))
n <- nrow(mat)
p <- ncol(mat)
mat <- sapply(1:p, function(j){
.cleanup_vector(mat[,j])
})
return(mat)
}
.cleanup_na_vector <- function(vec){
n <- length(vec)
idx <- which(is.na(vec))
if(length(idx) == n) vec <- rep(0, n)
if(length(idx) > 0){
vec[idx] <- stats::median(vec, na.rm = TRUE)
}
return(vec)
}
glmnet, if you look at https://cran.r-project.org/web/packages/glmnet/glmnet.pdf,
you’ll see the documentation for all the functions@export tag for!man folder (short for
“manual”).Rd) are what’s
in the man folder.man folder.
% Generated by roxygen2: do not edit by hand in the
rmult.Rd file.The pipeline (which we will talk in more detail later in course) is:
.R file under the
R folder in your packageman folder? See the next slide!After you do all this hard to write all these R functions and Roxygen documentation, how do all these other files get auto-generated?
Let’s use our UW561S2024Example package as an example.
(This is the same as UW561S2025Example package, except from
2024.)
Let’s say our current R package looks like this:
Then, you can run devtools::check() in the R console
(not in your R markdown file). Make sure you’re in your
R project for this R package.
Afterwards, you’ll see all these new files inside your folder that we were expecting:
See more with ?devtools::check to see how to you can
change how the function works.
devtools::check() actually does a lot
of things (which we’ll talk more about later in the course).
After you’ve run devtools::check(), you can run
devtools::load_all() to locally load all your functions in
your R folder. (Alternatively, if you weren’t in your R
project, you would need to run devtools::install() and then
load your package via library() as if it were any other
package.)
NAMESPACE fileThe NAMESPACE file is an important file for R packages,
but you will not need to create it yourself.
For example, if you look at the NAMESPACE for the
glmnet function, it looks like this (from https://github.com/cran/glmnet/blob/master/NAMESPACE):
As you can see, it says
# Generated by roxygen2: do not edit by hand. It is
automatically generated when you run
devtools::document().
.Rbuildignore file and
.gitignore file.Rbuildignore tells which files
devtools::check() should ignore.
.Rbuildignore.gitignore tells which files Git should ignore when
tracking changesvignettes
folder?Suppose I’m given this matrix that encodes a graph:
set.seed(10)
adj_mat <- matrix(stats::rbinom(n = 100,
size = 1,
prob = 0.25),
nrow = 10,
ncol = 10)
adj_mat <- adj_mat + t(adj_mat) #Symmeterize
adj_mat[adj_mat > 0] <- 1
diag(adj_mat) <- 0 #Remove self-edges
adj_mat## [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
## [1,] 0 0 1 0 0 0 0 0 1 0
## [2,] 0 0 0 0 0 1 0 0 0 1
## [3,] 1 0 0 0 0 0 1 0 1 0
## [4,] 0 0 0 0 0 0 1 1 0 0
## [5,] 0 0 0 0 0 0 1 0 0 1
## [6,] 0 1 0 0 0 0 1 0 0 1
## [7,] 0 0 1 1 1 1 0 0 1 0
## [8,] 0 0 0 1 0 0 0 0 0 0
## [9,] 1 0 1 0 0 0 1 0 0 0
## [10,] 0 1 0 0 1 1 0 0 0 0
Write a script where, given this specific graph:
(By the way, this graph looks like this):
Now that you have a high-level sense of how R packages works, the bigger question is: “How should I write my functions?”
Default values, as the name suggests, are when an argument has a default value when it is not explicitly defined the function call.
# A very simple (unnecessary) function that computes the quantile of a vector
demo_function <- function(x, quantile_value){
stats::quantile(x, probs = quantile_value)
}
# Again, try to avoid having variables the same as function names
# (That is why I named the variable "quantile_value", not "quantile")
set.seed(0)
x <- stats::rnorm(100)
demo_function(x, quantile_value = 0.9)## 90%
## 1.239882
If we knew that most of the time, we would be computing the 90%
quantile, then we could set quantile=0.9 directly.
demo_function <- function(x, quantile_value = 0.9){
stats::quantile(x, probs = quantile_value)
}
set.seed(0)
x <- stats::rnorm(100)
# no need to define the quantile!
demo_function(x) ## 90%
## 1.239882
## 50%
## -0.03296148
Many functions you use day-to-day have many default values
This one is from stats::lm. We see that the argument
method, model, qr, etc.
(arguments you probably didn’t even realize, or probably never set
yourself) have default arguments!
See https://www.rdocumentation.org/packages/stats/versions/3.6.2/topics/lm
Default values are great when you’re done with your project and am ready to release your codebase to the world (since it means other users need to worry about less arguments).
HOWEVER: While default values are convenient (i.e., you set them once, and never need to worry about them again), SET DEFAULT VALUES TO YOUR FUNCTIONS SPARINGLY while you’re working on your research project.
By avoiding using default values during your project’s development, you force yourself to declare all your arguments upfront, so you never are uncertain which arguments use which values.
(Yes: Your code will look uglier if you avoid setting default values. But also, yes, future-you will be grateful for this.)
A few tips on how to return things in your functions:
return() functionUnlike C++ and Java, functions in R do not check the class of the outputs.
NA or NaN.You might’ve noticed that I sometimes put a . before the
function argument. For example, the .cleanup_na_vector()
function:
cleanup_na_matrix <- function(mat){
stopifnot(is.matrix(mat), all(is.numeric(mat)))
n <- nrow(mat)
p <- ncol(mat)
mat <- sapply(1:p, function(j){
.cleanup_vector(mat[,j])
})
return(mat)
}
.cleanup_na_vector <- function(vec){
n <- length(vec)
idx <- which(is.na(vec))
if(length(idx) == n) vec <- rep(0, n)
if(length(idx) > 0){
vec[idx] <- stats::median(vec, na.rm = TRUE)
}
return(vec)
}
Curiously enough, Google’s style-guide also mentioned this:
What on earth is this about?
When I run these lines of code, notice the
.cleanup_na_vector() is “hidden” from the environment.
When you put a . in front of your function, you are
making the intent of the function clear – this function
is NOT meant for anyone else except the author (you) to
use.
This is “weaker” (i.e., not as explicit) as not exporting your
function via @export using Roxygen, but it has a similar
effect.
Another odd thing you might’ve realized is that I use ::
in my functions:
.cleanup_na_vector <- function(vec){
n <- length(vec)
idx <- which(is.na(vec))
if(length(idx) == n) vec <- rep(0, n)
if(length(idx) > 0){
vec[idx] <- stats::median(vec, na.rm = TRUE)
}
return(vec)
}
Curiously, Google’s Style-guide also states this and explains why:
Reason 1: So someone else can understand your code. Take this example of my code from 2019:
clique_selection <- function(...){ # some arguments
d <- vcount(g)
adj <- as.matrix(as_adjacency_matrix(g))
clique_list <- lapply(maximal.cliques(g), function(x){
sort(as.numeric(x))
})
if(any(!is.na(target_idx))) {
clique_list <- prune_clique(adj,
clique_list,
target_idx,
threshold)
}
if(length(clique_list) == 1) return(list(1:ncol(adj)))
largest_clique <- list(sort(clique_list[[
which.max(sapply(clique_list, length))
]]))
len <- length(clique_list)
queue <- initialize_queue(len)
hash_history <- hash()
hash_children <- initialize_children(clique_list)
hash_unique <- initialize_unique(clique_list, d)
# ... some more lines later on
}
If you saw this code for the first time, there are so many new functions, you might be very confused – which functions did Kevin write, and which functions did he use a package for?
Instead, if it were written as:
clique_selection <- function(...){ # some arguments
d <- igraph::vcount(g)
adj <- as.matrix(igraph::as_adjacency_matrix(g))
clique_list <- lapply(igraph::maximal.cliques(g), function(x){
sort(as.numeric(x))
})
if(any(!is.na(target_idx))) {
clique_list <- prune_clique(adj,
clique_list,
target_idx,
threshold)
}
if(length(clique_list) == 1) return(list(1:ncol(adj)))
largest_clique <- list(sort(clique_list[[
which.max(sapply(clique_list, length))
]]))
len <- length(clique_list)
queue <- initialize_queue(len)
hash_history <- hash::hash()
hash_children <- initialize_children(clique_list)
hash_unique <- initialize_unique(clique_list, d)
# ... some more lines later on
}
Then you can be like, “Aha, I see, so I only need to worry about
understanding all the function without a ::, since those
are the functions that Kevin must’ve defined somewhere.”
Reason 2: There might be a function name that used in multiple packages, and you want to make sure R uses the correct one.
Consider the example of compute the difference between extreme left-tail areas of a standard Gaussian. (We’re 29 standard deviations away from the mean!! These values are tiny)
## [1] 3.110291e-185
## [1] 3.110291e-185
However, what if we specifically wanted to use the pnorm
function in Rmpfr package (a package used for extreme
scientific computing)?
## Warning: package 'Rmpfr' was built under R version 4.3.3
## Warning: package 'gmp' was built under R version 4.3.3
x1 <- Rmpfr::mpfr(29.1, precBits = 1000)
x2 <- Rmpfr::mpfr(29.0, precBits = 1000)
Rmpfr::pnorm(x1) - Rmpfr::pnorm(x2)## 1 'mpfr' number of precision 1000 bits
## [1] 3.11029098704306832744474436058609456171386153827002784398827445343783468205809241614918290447477493715533670078051481511208922767421005799290297947391098211100990256393690562080315015040624013303799996522960755454471672441838758748773990322372780718078380051229122166634344894041531378421329369285889169e-185
compute_snns <- function(input_obj,
latent_k,
num_neigh,
bool_cosine = T,
bool_intersect = T,
min_deg = 1,
tol = 1e-4,
verbose = 0){
# ...
}There’s a sense that the input object (input_obj) is the
“most important” argument, hence it’s first.
This personal heuristic is great for exported functions (i.e., “public” functions you expect other people to use), since it’ll make their life easier.
HOWEVER: For “private” functions (i.e., functions
you name with a . in front, that you don’t expect other
people to use), it is very easy to make a coding error
by forgetting what the literal order of arguments is.
A very common way to introduce bugs is that you passed in the arguments in wrong order!!
Here are the rules I follow (whenever I remember) for “private” functions:
.tiltedCCA_common_score <- function(averaging_mat,
cca_res,
discretization_gridsize,
enforce_boundary,
fix_tilt_perc,
snn_bool_cosine,
snn_bool_intersect,
snn_k,
snn_min_deg,
snn_num_neigh,
svd_1,
svd_2,
target_dimred,
verbose = 0){
# ...
}# some code in some other function
res <- .tiltedCCA_common_score(averaging_mat = averaging_mat,
cca_res = cca_res,
discretization_gridsize = discretization_gridsize,
enforce_boundary = enforce_boundary,
fix_tilt_perc = fix_tilt_perc,
snn_bool_cosine = snn_bool_cosine,
snn_bool_intersect = snn_bool_intersect,
snn_k = snn_k,
snn_min_deg = snn_min_deg,
snn_num_neigh = snn_num_neigh,
svd_1 = svd_1,
svd_2 = svd_2,
target_dimred = target_dimred,
verbose = verbose)Yes, again, this makes your code uglier. But, also yes, your future-self will thank current-you for this.
On this aspect, I don’t have much strong opinions on. Here is what I do:
_ to separate words in my variables or
functions (instead of CamelCase). Avoid using . to separate
words. (More on this in a bit!)clustering_kmeans_result and
clustering_dbscan_result (instead of
kmeans_clustering_result and
dbscan_clustering_result)<- instead of
= when I set variables. This is purely stylistic choice. (I
have not yet found a very good reason on why you should use one over
another.)stats::lm() function work?One last thing before we end today’s lecture.
stats::lm()
function work?ctl <- c(4.17,5.58,5.18,6.11,4.50,4.61,5.17,4.53,5.33,5.14)
trt <- c(4.81,4.17,4.41,3.59,5.87,3.83,6.03,4.89,4.32,4.69)
group <- gl(2, 10, 20, labels = c("Ctl","Trt"))
weight <- c(ctl, trt)
lm_D9 <- stats::lm(weight ~ group)lm_D9 is a list.
## [1] TRUE
## [1] "coefficients" "residuals" "effects" "rank"
## [5] "fitted.values" "assign" "qr" "df.residual"
## [9] "contrasts" "xlevels" "call" "terms"
## [13] "model"
Somehow, when we run print() on lm_D9, we
can some informative text.
##
## Call:
## stats::lm(formula = weight ~ group)
##
## Coefficients:
## (Intercept) groupTrt
## 5.032 -0.371
But this isn’t the normal behavior of a list…
## $a
## [1] 1 2 3 4 5 6 7 8 9 10
##
## $b
## [1] "asdf"
Somehow, when we run plot() on lm_D9, we
can some informative plots.
But this isn’t the normal behavior of a list…
## Error in xy.coords(x, y, xlabel, ylabel, log): 'x' is a list, but does not have components 'x' and 'y'
Somehow, when we run summary() on lm_D9, we
can an informative summary.
##
## Call:
## stats::lm(formula = weight ~ group)
##
## Residuals:
## Min 1Q Median 3Q Max
## -1.0710 -0.4938 0.0685 0.2462 1.3690
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 5.0320 0.2202 22.850 9.55e-15 ***
## groupTrt -0.3710 0.3114 -1.191 0.249
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 0.6964 on 18 degrees of freedom
## Multiple R-squared: 0.07308, Adjusted R-squared: 0.02158
## F-statistic: 1.419 on 1 and 18 DF, p-value: 0.249
But this isn’t the normal behavior of a list…
## Length Class Mode
## a 10 -none- numeric
## b 1 -none- character
What is going on? How does this happen?
The result of stats::lm() actually are of class
lm.
## [1] "lm"
It’s actually more than just a list!
Furthermore, when you call print(lm_D9), you’re actually
using the function print.lm(). See https://github.com/SurajGupta/r-source/blob/master/src/library/stats/R/lm.R#L248
But… how did we actually end up using print.lm() without
explicitly calling it?
. to separate words in your
function name. The . has a special meaning in R. The usual
structure is [function_name].[class].## [1] print.acf*
## [2] print.activeConcordance*
## [3] print.AES*
## [4] print.all_vars*
## [5] print.anova*
## [6] print.any_vars*
## [7] print.aov*
## [8] print.aovlist*
## [9] print.ar*
## [10] print.Arima*
## [11] print.arima0*
## [12] print.AsIs
## [13] print.aspell*
## [14] print.aspell_inspect_context*
## [15] print.bibentry*
## [16] print.Bibtex*
## [17] print.bigq*
## [18] print.bigz*
## [19] print.browseVignettes*
## [20] print.bslib_breakpoints*
## [21] print.bslib_fragment*
## [22] print.bslib_navbar_options*
## [23] print.bslib_page*
## [24] print.bslib_showcase_layout*
## [25] print.bslib_value_box_theme*
## [26] print.by
## [27] print.cachem*
## [28] print.changedFiles*
## [29] print.check_bogus_return*
## [30] print.check_code_usage_in_package*
## [31] print.check_compiled_code*
## [32] print.check_demo_index*
## [33] print.check_depdef*
## [34] print.check_details*
## [35] print.check_details_changes*
## [36] print.check_doi_db*
## [37] print.check_dotInternal*
## [38] print.check_make_vars*
## [39] print.check_nonAPI_calls*
## [40] print.check_package_code_assign_to_globalenv*
## [41] print.check_package_code_attach*
## [42] print.check_package_code_data_into_globalenv*
## [43] print.check_package_code_startup_functions*
## [44] print.check_package_code_syntax*
## [45] print.check_package_code_unload_functions*
## [46] print.check_package_compact_datasets*
## [47] print.check_package_CRAN_incoming*
## [48] print.check_package_datalist*
## [49] print.check_package_datasets*
## [50] print.check_package_depends*
## [51] print.check_package_description*
## [52] print.check_package_description_encoding*
## [53] print.check_package_license*
## [54] print.check_packages_in_dir*
## [55] print.check_packages_used*
## [56] print.check_po_files*
## [57] print.check_pragmas*
## [58] print.check_Rd_line_widths*
## [59] print.check_Rd_metadata*
## [60] print.check_Rd_xrefs*
## [61] print.check_RegSym_calls*
## [62] print.check_S3_methods_needing_delayed_registration*
## [63] print.check_so_symbols*
## [64] print.check_T_and_F*
## [65] print.check_url_db*
## [66] print.check_vignette_index*
## [67] print.checkDocFiles*
## [68] print.checkDocStyle*
## [69] print.checkFF*
## [70] print.checkRd*
## [71] print.checkRdContents*
## [72] print.checkReplaceFuns*
## [73] print.checkS3methods*
## [74] print.checkTnF*
## [75] print.checkVignettes*
## [76] print.citation*
## [77] print.cli_ansi_html_style*
## [78] print.cli_ansi_string*
## [79] print.cli_ansi_style*
## [80] print.cli_boxx*
## [81] print.cli_diff_chr*
## [82] print.cli_doc*
## [83] print.cli_progress_demo*
## [84] print.cli_rule*
## [85] print.cli_sitrep*
## [86] print.cli_spark*
## [87] print.cli_spinner*
## [88] print.cli_tree*
## [89] print.codoc*
## [90] print.codocClasses*
## [91] print.codocData*
## [92] print.cohesiveBlocks*
## [93] print.col_spec*
## [94] print.collector*
## [95] print.colorConverter*
## [96] print.communities*
## [97] print.compactPDF*
## [98] print.condition
## [99] print.connection
## [100] print.CRAN_package_reverse_dependencies_and_views*
## [101] print.css*
## [102] print.data.frame
## [103] print.Date
## [104] print.date_names*
## [105] print.default
## [106] print.dendrogram*
## [107] print.density*
## [108] print.difftime
## [109] print.dist*
## [110] print.Dlist
## [111] print.DLLInfo
## [112] print.DLLInfoList
## [113] print.DLLRegisteredRoutines
## [114] print.document_context*
## [115] print.document_position*
## [116] print.document_range*
## [117] print.document_selection*
## [118] print.dplyr_join_by*
## [119] print.dplyr_sel_vars*
## [120] print.dummy_coef*
## [121] print.dummy_coef_list*
## [122] print.ecdf*
## [123] print.eigen
## [124] print.element*
## [125] print.evaluate_evaluation*
## [126] print.factanal*
## [127] print.factor
## [128] print.family*
## [129] print.fileSnapshot*
## [130] print.findLineNumResult*
## [131] print.flatGridListing*
## [132] print.formula*
## [133] print.fseq*
## [134] print.ftable*
## [135] print.fun_list*
## [136] print.function
## [137] print.getAnywhere*
## [138] print.ggplot*
## [139] print.ggplot2_bins*
## [140] print.ggproto*
## [141] print.ggproto_method*
## [142] print.gList*
## [143] print.glm*
## [144] print.glue*
## [145] print.gpar*
## [146] print.GridCoords*
## [147] print.GridGrobCoords*
## [148] print.GridGTreeCoords*
## [149] print.grob*
## [150] print.gtable*
## [151] print.hashtab*
## [152] print.hcl_palettes*
## [153] print.hclust*
## [154] print.help_files_with_topic*
## [155] print.hexmode
## [156] print.hms*
## [157] print.HoltWinters*
## [158] print.hsearch*
## [159] print.hsearch_db*
## [160] print.htest*
## [161] print.html*
## [162] print.html_dependency*
## [163] print.htmltools.selector*
## [164] print.htmltools.selector.list*
## [165] print.igraph*
## [166] print.igraph_layout_modifier*
## [167] print.igraph_layout_spec*
## [168] print.igraph.es*
## [169] print.igraph.vs*
## [170] print.igraphHRG*
## [171] print.igraphHRGConsensus*
## [172] print.infl*
## [173] print.integrate*
## [174] print.integrateR*
## [175] print.isoreg*
## [176] print.json*
## [177] print.key_missing*
## [178] print.kmeans*
## [179] print.knitr_kable*
## [180] print.last_dplyr_warnings*
## [181] print.Latex*
## [182] print.LaTeX*
## [183] print.libraryIQR
## [184] print.lifecycle_warnings*
## [185] print.listof
## [186] print.lm*
## [187] print.loadings*
## [188] print.locale*
## [189] print.loess*
## [190] print.logLik*
## [191] print.ls_str*
## [192] print.medpolish*
## [193] print.membership*
## [194] print.MethodsFunction*
## [195] print.mpfr*
## [196] print.mpfr1*
## [197] print.mpfrArray*
## [198] print.mtable*
## [199] print.NativeRoutineList
## [200] print.Ncharacter*
## [201] print.news_db*
## [202] print.nls*
## [203] print.noquote
## [204] print.numeric_version
## [205] print.object_size*
## [206] print.octmode
## [207] print.packageDescription*
## [208] print.packageInfo
## [209] print.packageIQR*
## [210] print.packageStatus*
## [211] print.paged_df*
## [212] print.pairwise.htest*
## [213] print.path*
## [214] print.person*
## [215] print.pillar*
## [216] print.pillar_1e*
## [217] print.pillar_colonnade*
## [218] print.pillar_ornament*
## [219] print.pillar_shaft*
## [220] print.pillar_squeezed_colonnade*
## [221] print.pillar_tbl_format_setup*
## [222] print.pillar_vctr*
## [223] print.pillar_vctr_attr*
## [224] print.POSIXct
## [225] print.POSIXlt
## [226] print.power.htest*
## [227] print.ppr*
## [228] print.prcomp*
## [229] print.princomp*
## [230] print.proc_time
## [231] print.purrr_function_compose*
## [232] print.purrr_function_partial*
## [233] print.purrr_rate_backoff*
## [234] print.purrr_rate_delay*
## [235] print.quosure*
## [236] print.quosures*
## [237] print.R6*
## [238] print.R6ClassGenerator*
## [239] print.raster*
## [240] print.Rconcordance*
## [241] print.Rd*
## [242] print.recordedplot*
## [243] print.rel*
## [244] print.restart
## [245] print.RGBcolorConverter*
## [246] print.RGlyphFont*
## [247] print.rlang_box_done*
## [248] print.rlang_box_splice*
## [249] print.rlang_data_pronoun*
## [250] print.rlang_dict*
## [251] print.rlang_dyn_array*
## [252] print.rlang_envs*
## [253] print.rlang_error*
## [254] print.rlang_fake_data_pronoun*
## [255] print.rlang_lambda_function*
## [256] print.rlang_message*
## [257] print.rlang_trace*
## [258] print.rlang_warning*
## [259] print.rlang_zap*
## [260] print.rlang:::list_of_conditions*
## [261] print.rle
## [262] print.rlib_bytes*
## [263] print.rlib_error_3_0*
## [264] print.rlib_trace_3_0*
## [265] print.roman*
## [266] print.sass*
## [267] print.sass_bundle*
## [268] print.sass_layer*
## [269] print.scalar*
## [270] print.sessionInfo*
## [271] print.shiny.tag*
## [272] print.shiny.tag.env*
## [273] print.shiny.tag.list*
## [274] print.shiny.tag.query*
## [275] print.simple.list
## [276] print.smooth.spline*
## [277] print.socket*
## [278] print.src*
## [279] print.srcfile
## [280] print.srcref
## [281] print.stepfun*
## [282] print.stl*
## [283] print.stringr_view*
## [284] print.StructTS*
## [285] print.subdir_tests*
## [286] print.summarize_CRAN_check_status*
## [287] print.summary.aov*
## [288] print.summary.aovlist*
## [289] print.summary.ecdf*
## [290] print.summary.glm*
## [291] print.summary.lm*
## [292] print.summary.loess*
## [293] print.summary.manova*
## [294] print.summary.nls*
## [295] print.summary.packageStatus*
## [296] print.summary.ppr*
## [297] print.summary.prcomp*
## [298] print.summary.princomp*
## [299] print.summary.table
## [300] print.summary.warnings
## [301] print.summaryDefault
## [302] print.summaryMpfr*
## [303] print.table
## [304] print.tables_aov*
## [305] print.tbl*
## [306] print.terms*
## [307] print.theme*
## [308] print.tidyverse_conflicts*
## [309] print.tidyverse_logo*
## [310] print.transform*
## [311] print.trunc_mat*
## [312] print.ts*
## [313] print.tskernel*
## [314] print.TukeyHSD*
## [315] print.tukeyline*
## [316] print.tukeysmooth*
## [317] print.undoc*
## [318] print.uneval*
## [319] print.unit*
## [320] print.vctrs_bytes*
## [321] print.vctrs_sclr*
## [322] print.vctrs_unspecified*
## [323] print.vctrs_vctr*
## [324] print.viewport*
## [325] print.vignette*
## [326] print.warnings
## [327] print.xfun_md_viewable*
## [328] print.xfun_raw_string*
## [329] print.xfun_record_results*
## [330] print.xfun_rename_seq*
## [331] print.xfun_strict_list*
## [332] print.xgettext*
## [333] print.xngettext*
## [334] print.xtabs*
## see '?methods' for accessing help and source code
print(), plot(), and
summary() functions are actually “generics”.print() is defined in base R (see https://github.com/SurajGupta/r-source/blob/master/src/library/base/R/print.R):This UseMethod() function means: whenever the
print() function is called, R looks up the class of the
first argument and “dispatches” the correct print.[class]
function.
For most generic functions, there’s usually a
default function. For example, print.default()
is the print statement that is used when R cannot find a suitable other
print() function to use. See https://github.com/SurajGupta/r-source/blob/master/src/library/base/R/print.R#L22)
structure()UW561, so you can write your own print.UW561()
function):create_561 <- function(string){
structure(string,
class = "UW561")
}
print.UW561 <- function(x, ...){
paste("Our custom 561 print statement:", x)
}
result <- create_561("test")
result## Our custom 561 print statement: test
You can do this with all your fancy results!
If you want to enumerate all the generic functions, see https://stackoverflow.com/questions/15803154/see-which-s3-generic-methods-are-available-in-environment
objs1 <- mget(ls("package:base"), inherits=TRUE)
funs1 <- Filter(is.function, objs1)
genFuns0 <- sapply(
funs1,
function(X) {
if (!is.null(body(X)) & !is.symbol(body(X))) {
utils::isS3stdGeneric(X)
}
},
simplify=FALSE
)
sort(names(Filter(isTRUE, genFuns0)))## [1] "%*%" "anyDuplicated"
## [3] "as.array" "as.Date"
## [5] "as.expression" "as.function"
## [7] "as.list" "as.matrix"
## [9] "as.null" "as.POSIXct"
## [11] "as.POSIXlt" "as.single"
## [13] "as.table" "by"
## [15] "chol" "chooseOpsMethod"
## [17] "close" "conditionCall"
## [19] "conditionMessage" "cut"
## [21] "date" "determinant"
## [23] "diff" "droplevels"
## [25] "duplicated" "flush"
## [27] "getDLLRegisteredRoutines" "intersect"
## [29] "is.na<-" "isSymmetric"
## [31] "julian" "kappa"
## [33] "labels" "levels"
## [35] "matrix" "merge"
## [37] "months" "mtfrm"
## [39] "nameOfClass" "open"
## [41] "plot" "pretty"
## [43] "print" "qr"
## [45] "quarters" "rev"
## [47] "row.names" "row.names<-"
## [49] "rowsum" "scale"
## [51] "seek" "seq"
## [53] "sequence" "setdiff"
## [55] "setequal" "solve"
## [57] "split" "split<-"
## [59] "subset" "toString"
## [61] "transform" "truncate"
## [63] "union" "units"
## [65] "units<-" "weekdays"
## [67] "with" "within"
If you write your own class, you can then write your class’s specific version for any of these!